#import "Calculette.h"
#import "Printer.h"
#import "Info.h"

@implementation Calculette

+ initialize
{
  static NXDefaultsVector CalculetteDefaults = {
    "Printed",		"0",
    "Fixed",		"0",
    "Precision",	"2",
    0,			0,
  };

  NXRegisterDefaults( "Calculette", CalculetteDefaults );

  return self;
}

- display
{
  char s[ 64 ];

  if ( fixed )
    sprintf( s, "%.*f", precision, display );
  else
    sprintf( s, "%.10g", display );

  [ text setStringValue:s ];

  if ( memory ) 	/* redisplay memory indicator on top */
    [ memoryIndicator display ];

  return self;
}

- clear:sender
{
  if ( display == 0.0 ) {
    value = 0.0;
    entered = NO;
  }

  input = display = 0.0;
  decimals = 0;

  [ self display ];

  return self;
}

- digit:(int)n
{
  if ( entered ) {
    entered = NO;
    decimals = 0;
    display = 0.0;
  }

  if ( decimals )
    display = display + ( n / pow( 10, decimals++ ));
  else
    display = display * 10 + n;

  [ self display ];

  return self;
}

- point:sender
{
  if ( entered ) {
    entered = NO;
    decimals = 1;
    display = 0.0;
  }
  else {
    if ( decimals )
      NXBeep( );
    else
      decimals = 1;
  }

  [ self display ];

  return self;
}

- operation:(int)op
{
  if ( !entered ) {
    input = display;
    entered = YES;
  }

  switch( op ) {
  case '+' :
    value += input;
    break;
  case '-':
    value -= input;
    break;
  case '*':
    value *= input;
    break;
  case '/':
    if ( input == 0.0 )
      NXBeep( );
    else
      value /= input;
    break;
  case '=' :
    break;
  default:
    break;
  }

  display = value;

  [ self display ];

  if ( printed ) {
    char s[ 64 ];

    if ( fixed )
      sprintf( s, "%c\t%.*f\n", op, precision, op == '=' ? value : input );
    else
      sprintf( s, "%c\t%.10g\n", op, op == '=' ? value : input );

    [ printer appendToText:s ];
  }

  return self;
}

- percent:sender
{
  if ( !entered ) {
    input = display;
    entered = YES;
  }

  display = value * input / 100.0;

  [ self display ];

  input = display;

  return self;
}

- zero:sender		{ return [ self digit:0 ]; }
- one:sender		{ return [ self digit:1 ]; }
- two:sender		{ return [ self digit:2 ]; }
- three:sender		{ return [ self digit:3 ]; }
- four:sender		{ return [ self digit:4 ]; }
- five:sender		{ return [ self digit:5 ]; }
- six:sender		{ return [ self digit:6 ]; }
- seven:sender		{ return [ self digit:7 ]; }
- eight:sender		{ return [ self digit:8 ]; }
- nine:sender		{ return [ self digit:9 ]; }

- plus:sender		{ return [ self operation:'+' ]; }
- minus:sender		{ return [ self operation:'-' ]; }
- multiply:sender	{ return [ self operation:'*' ]; }
- divide:sender		{ return [ self operation:'/' ]; }

- equal:sender		{ return [ self operation:'=' ]; }

- memoryRecall:sender;
{
  input = display = memory;
  entered = YES;

  [ self display ];

  return self;
}

- memoryClear:sender
{
  memory = 0.0;

  [ memoryIndicator setStringValueNoCopy:0 ];

  return self;
}

- memoryPlus:sender;
{
  memory += display;

  [ memoryIndicator setStringValueNoCopy:memory ? "M" : 0  ];

  return self;
}

- memoryMinus:sender;
{
  memory -= display;

  [ memoryIndicator setStringValueNoCopy:memory ? "M" : 0  ];

  return self;
}

- fix:sender
{
  fixed = fixed ? NO : YES;

  [ self display ];

  return self;
}

- precision:sender
{
  precision = [[ sender selectedCell ] tag ];

  [ self display ];

  return self;
}

- showInfoPanel:sender
{
  if ( !infoPanel ) 
    if ( ![ NXApp loadNibSection:"Info.nib" owner:self ] )
      return nil;

  if ( ![ infoPanel isVisible ] )
    [ infoPanel center ];

  [ infoPanel makeKeyAndOrderFront:nil ];

  [ infoView animateInfo:self ];
  
  return self;
}

- (BOOL)menuActive:menuCell
{
  BOOL	shouldBeEnabled = printer ? YES : NO;

  if ( [ menuCell isEnabled ] != shouldBeEnabled ) {
    [ menuCell setEnabled:shouldBeEnabled ];
    return YES;
  }
  return NO;
}

- (BOOL)revertToSavedMenuActive:menuCell
{
  BOOL	shouldBeEnabled = (printer ? YES : NO)
    && [[ printer window ] isDocEdited ]
      && [ printer file ];

  if ( [ menuCell isEnabled ] != shouldBeEnabled ) {
    [ menuCell setEnabled:shouldBeEnabled ];
    return YES;
  }
  return NO;
}

- showPrefPanel:sender
{
  NXRect calcFrame;
  NXCoord x, y;

  if ( !prefPanel ) 
    if ( ![ NXApp loadNibSection:"Preferences.nib" owner:self ] )
      return nil;

  [ window getFrame:&calcFrame ];

  x = NX_X( &calcFrame ) + 39;
  y = NX_Y( &calcFrame ) + 22;

  [ prefPanel moveTo:x :y ];
  [ prefPanel makeKeyAndOrderFront:nil ];

  return self;
}

- newPrinter:sender
{
  NXRect baseFrame;
  NXCoord x, y;

  if ( ![ NXApp loadNibSection:"Printer.nib" owner:self ] )
    return nil;

  [[ printer text ] setDelegate:[[ printer window ] delegate ]]; // IB (3.1) could not do it
  [[ printer text ] setSel:0 :0 ];

  if ( listOfPrinters == 0 )
    listOfPrinters = [[ List alloc ] init ];

  if ( [ listOfPrinters count ] ) {
    [[[ listOfPrinters lastObject ] window ] getFrame:&baseFrame ];

    x = NX_X( &baseFrame ) + 22;
    y = NX_Y( &baseFrame ) - 24;
  }
  else {
    [ window getFrame:&baseFrame ];

    x = NX_X( &baseFrame ) + NX_WIDTH( &baseFrame ) - 1;
    y = NX_Y( &baseFrame );
  }

  [[ printer window ] moveTo:x :y ];
  [[ printer window ] makeKeyAndOrderFront:nil ];

  [ listOfPrinters addObject:printer ];

  return self;
}

- openPrinter:sender
{
  char *types[ 2 ] = { "calculette", 0 };
  OpenPanel *openPanel = [ OpenPanel new ];
  
  if ( [ openPanel runModalForTypes:types ] )
    if ( [ self newPrinter:self ] )
      [ printer load:[ openPanel filename ] ];

  return self;
}

- copy:sender
{
  Pasteboard *pboard;
  NXAtom types[ 1 ];

  const char *s = [ text stringValue ];

  types[ 0 ] = NXAsciiPboardType;

  pboard = [ Pasteboard new ];
  [ pboard declareTypes:types num:2 owner:0 ];
  [ pboard writeType:NXAsciiPboardType data:s length:strlen( s ) ];

  return self;
}

- paste:sender
{
  Pasteboard *pboard;
  NXAtom types[ 1 ];

  const char *data;
  int length;

  double dval;

  types[ 0 ] = NXAsciiPboardType;
  
  pboard = [ Pasteboard new ];

  if ( [ pboard findAvailableTypeFrom:types num:1 ] )
    if ( [ pboard readType:NXAsciiPboardType data:&data length:&length ] )
      if ( sscanf( data, "%lf", &dval ) == 1 ) {
	input = display = dval;	// just like a memory recall
	entered = YES;

	[ self display ];

	return self;
      }

  NXBeep( );

  return nil;
}

- cut:sender
{
  [[ self copy:sender ] clear:sender ];

  return self;
}

- print:sender
{
  printed = printed ? NO : YES;

  if ( printed ) {
    if ( !printer ) {
      [ self newPrinter:self ];
      [[ printer text ] setSel:0 :0 ];
    }
    else
      [[ printer window ] orderFront:sender ];
  }

  return self;
}

- removePrinter:sender
{
  if ( sender == printer ) {
    printed = NO;
    printer = nil;
    [ printerButton setState:printed ];
  }

  [ listOfPrinters removeObject:sender ];

  return self;
}

- changePrinter:sender
{
  printer = sender;

  return self;
}

@end

@implementation Calculette( WindowDelegate )

- windowDidMove:sender
{
  [ window saveFrameUsingName:"Calculette" ];

  return self;
}

@end

@implementation Calculette( ApplicationDelegate )

- appDidInit:sender
{
  NXImage *miniwindowImage = [[ NXImage alloc ] initFromSection:"PrinterM.tiff" ];

  printed = strcmp( NXGetDefaultValue( "Calculette", "Printed" ), "1" ) == 0 ? YES : NO;
  [ printerButton setState:printed ];

  fixed = strcmp( NXGetDefaultValue( "Calculette", "Fixed" ), "1" ) == 0 ? YES : NO;
  [ fixButton setState:fixed ];

  precision = atoi( NXGetDefaultValue( "Calculette", "Precision" ));
  [[ precisionPopUpList itemList ] selectCellWithTag:precision ];
  [ precisionPopUpButton setTitle:[[[ precisionPopUpList itemList ] selectedCell ] title ]];

  [ self display ]; // format the display register according to preferences

  [ window setFrameUsingName:"Calculette" ];
  [ window makeKeyAndOrderFront:nil ];

  if ( [ listOfPrinters count ] ) { /* launched by Workspace */
    NXRect baseFrame;
    NXCoord x, y;

    [ window getFrame:&baseFrame ];

    x = NX_X( &baseFrame ) + NX_WIDTH( &baseFrame ) - 1;
    y = NX_Y( &baseFrame );

    [[ printer window ] moveTo:x :y ];
  }

  if ( miniwindowImage )
    [ miniwindowImage setName:"Miniprinter" ];

  [ saveMenuCell setUpdateAction:@selector( menuActive: ) forMenu:printerMenu ];
  [ saveAsMenuCell setUpdateAction:@selector( menuActive: ) forMenu:printerMenu ];
  [ revertToSavedMenuCell setUpdateAction:@selector( revertToSavedMenuActive: ) forMenu:printerMenu ];
  [ pageLayoutMenuCell setUpdateAction:@selector( menuActive: ) forMenu:formatMenu ];
  [ printMenuCell setUpdateAction:@selector( menuActive: ) forMenu:[NXApp mainMenu ]];

  [ NXApp setAutoupdate:YES ];

  if ( printed )
    [ self newPrinter:self ];

  return self;
}

- (BOOL)appAcceptsAnotherFile:sender
{
  return YES;
}

- (int)app:sender openFile:(const char *)aFile type:(const char *)aType
{
  if ( [ self newPrinter:self ] )
    if ( [ printer load:aFile ] )
      return YES;

  return NO;
}

- appWillTerminate:sender
{
  int i;
  id winList = [ NXApp windowList ];

  for ( i = 0; i < [ winList count ]; i++ ) {
    id win = [ winList objectAt:i ];

    if ( [[ win delegate ] isKindOf:[ Printer class ]] && [ win isDocEdited ] ) {
      switch ( NXRunAlertPanel( "Quit", "There are edited windows.", "Review Unsaved", "Quit Anyway", "Cancel" )) {
      case NX_ALERTDEFAULT:
	do {
	  id win = [ winList objectAt:i ];
	  if ( [[ win delegate ] isKindOf:[ Printer class ]] )
	    [ win performClose:nil ];
	} while ( ++i < [ winList count ] );

	return self;

      case NX_ALERTALTERNATE:
	return self;

      case NX_ALERTOTHER:
      default:
	return nil;
      }
    }
  }
  return self;
}

@end
